<template>
  <div class="container" :style="containerStyle">
    <div class="wrapper" :style="wrapperStyle">
      <div class="row" v-for="(row, rowIndex) in photos">
        <div class="col" v-for="(col, colIndex) in row" :style="cellStyle" @click="photoClick(rowIndex, colIndex)">
          <div class="inner">
            <img :src="images[col]" v-if="images[col]"/>
            <div class="title" v-else>{{photos[rowIndex][colIndex]}}</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>

/**
 * 缩放平移照片墙
 * ZoomTranslatePhotoWall
 * v1.00 完成基本功能 2018-10-17
 */

export default {
  name: 'ZoomTranslatePhotoWall',
  props: {
    viewWidth: {
      type: Number,
      default: 375
    },
    viewHeight: {
      type: Number,
      default: 210
    },
    shrinkSize: {
      type: Number,
      default: 30
    },
    cardPadding: {
      type: Number,
      default: 2
    },
    scaleDuration: {
      type: Number,
      default: 600
    },
    translateDuration: {
      type: Number,
      default: 600
    },
    fullViewDuration: {
      type: Number,
      default: 2000
    },
    shrinkViewDuration: {
      type: Number,
      default: 500
    },
    images: {
      type: Array,
      default: () => []
    }
  },

  data() {
    return {
      wrapperStyle: {},
      cellStyle: {},
      photos: [],
      paths: [{x: 1, y: 1}]
    };
  },

  created() {
    this.cardWidth = this.viewWidth - 2 * this.shrinkSize
    this.cardHeight = this.viewHeight - 2 * this.shrinkSize
    this.scaleX = this.viewWidth / (this.cardWidth - 2 * this.cardPadding)
    this.scaleY = this.viewHeight / (this.cardHeight - 2 * this.cardPadding)
    this.viewCenterX = this.viewWidth / 2
    this.viewCenterY = this.viewHeight / 2
    this.containerStyle = {
      width: `${this.viewWidth}px`,
      height: `${this.viewHeight}px`
    },
    this.cellStyle = {
      width: `${this.cardWidth}px`,
      height: `${this.cardHeight}px`,
      padding: `${this.cardPadding}px`
    }
    this.loop = true
    this.createPhotoWall()
    this.goes()
  },

  methods: {

    photoClick(row, col) {
      this.$emit('click', {row, col})
      console.log('photoClick', {row, col})
    },

    async goes() {
      let init = true
      while(this.loop) {
        let row = this.paths[this.paths.length - 1].x
        let col = this.paths[this.paths.length - 1].y
        let cardCenterX = col * this.cardWidth + this.cardWidth / 2
        let cardCenterY = row * this.cardHeight + this.cardHeight / 2
        let translateX = this.viewCenterX - cardCenterX
        let translateY = this.viewCenterY - cardCenterY
        let transformOriginX = cardCenterX
        let transformOriginY = cardCenterY
        if (init) {
          // 展示首张画幅
          this.wrapperStyle = {
            transition: 'transform 0ms linear',
            transformOrigin: `${transformOriginX}px ${transformOriginY}px`,
            transform: `translate3d(${translateX}px, ${translateY}px, 0) scale(${this.scaleX}, ${this.scaleY})`
          }
          this.init = false
        }

        // 展示全窗口画幅
        await this.sleep(this.fullViewDuration)
        
        // 缩小画幅
        this.wrapperStyle = {
          transition: `transform ${this.scaleDuration}ms linear`,
          transformOrigin: `${transformOriginX}px ${transformOriginY}px`,
          transform: `translate3d(${translateX}px, ${translateY}px, 0) scale(1, 1)`
        }
        await this.sleep(this.scaleDuration + this.shrinkViewDuration)

        // 移动到下一幅
        this.getPaths()
        row = this.paths[this.paths.length - 1].x
        col = this.paths[this.paths.length - 1].y
        cardCenterX = col * this.cardWidth + this.cardWidth / 2
        cardCenterY = row * this.cardHeight + this.cardHeight / 2
        translateX = this.viewCenterX - cardCenterX
        translateY = this.viewCenterY - cardCenterY
        transformOriginX = cardCenterX
        transformOriginY = cardCenterY
        this.wrapperStyle = {
          transition: `transform ${this.translateDuration}ms linear`,
          transformOrigin: `${transformOriginX}px ${transformOriginY}px`,
          transform: `translate3d(${translateX}px, ${translateY}px, 0) scale(1, 1)`
        }
        await this.sleep(this.translateDuration + this.shrinkViewDuration)

        // 放大到整个窗口
        this.wrapperStyle = {
          transition: `transform ${this.scaleDuration}ms linear`,
          transformOrigin: `${transformOriginX}px ${transformOriginY}px`,
          transform: `translate3d(${translateX}px, ${translateY}px, 0) scale(${this.scaleX}, ${this.scaleY})`
        }
        await this.sleep(this.scaleDuration + 100)
      }
    },

    sleep(millisecond) {
      return new Promise((resolve, reject) => {
        this.timer = setTimeout(() => {
          resolve()
        }, millisecond)
      })
    },

    /**
     * 生成移动路径
     * 生成的移动路径，要避免先前刚刚移动过的图片由移动到
     * 但为了显示所有9张图片，在移动路径上不可避免会出现重复移到的图片
     * 处理原则是尽可能移动到没有移动到过的图片和最远距离移动过的图片
     */
    getPaths() {
      let moves = [
        {
          // direction: "top",
          x: 0,
          y: -1
        }, 
        {
          // direction: "right",
          x: 1,
          y: 0
        },
        {
          // direction: "bottom",
          x: 0,
          y: 1
        },
        {
          // direction: "left",
          x: -1,
          y: 0
        }
      ]
      
      let steps = [] // 可以移动到的方位数组
      let paths = this.paths
      let photos = this.photos
      while (moves.length) {
        let i = Math.floor(Math.random() * moves.length)
        let x = paths[paths.length - 1].x + moves[i].x
        let y = paths[paths.length - 1].y + moves[i].y
        moves.splice(i, 1)
        if (x < 1 || y < 1 || x >= photos.length - 1 || y >= photos.length - 1) {
          continue
        }
        let lastIndex = 9999 // 上一次移动到与当前的距离（移动次数），若从未移动到，则用最大数9999表示
        // 在历史paths数组中倒序查找上一次移动到的距离
        for (let i = paths.length - 1; i>=0; i--) {
          if (paths[i].x == x && paths[i].y == y) {
            lastIndex = paths.length - 1 - i
            break
          }
        }
        steps.push({x, y, lastIndex})
      }
      // 将所有可以移动的方位，按与上一次移动到距离最远降序排列
      steps.sort((x1, x2) => {
        return x2.lastIndex - x1.lastIndex
      })
      // 将与上一次移动到距离最远的移动方位当成最新（下一步）移动方位
      this.paths.push({
        x: steps[0].x,
        y: steps[0].y
      })
    },

    /**
      生成照片墙布局
      照片墙在收缩时，可以同时显示邻近的9张图片，因此任何相邻的9张图片都不可重复
      由于边缘图片不能移动到视窗中显示，为了能够完整显示序号为0-8的共9张图片，需要在其外围添加2行2列作为陪衬
      这样照片墙就形成了5x5的布局，中间可以再视窗中显示的9张图片是不重不漏的
     */
    createPhotoWall() {
      let photos = [];
      photos[0] = [0, 1, 2, 0, 1];
      photos[1] = [3, 4, 5, 3, 4];
      photos[2] = [6, 7, 8, 6, 7];
      photos[3] = [0, 1, 2, 0, 1];
      photos[4] = [3, 4, 5, 3, 4];
      this.photos = photos
    }
  }
};
</script>

<style lang="stylus" scoped>

.container
  width: 375px; /* 默认css布局，会被js设置覆盖 */
  height: 210px; /* 默认css布局，会被js设置覆盖 */
  overflow: hidden

  .wrapper
    width: 600%;
    height: 600%;

  .row
    display: flex
    .col
      flex-shrink: 0
      width: 315px /* 默认css布局，会被js设置覆盖 */
      height: 150px /* 默认css布局，会被js设置覆盖 */
      padding: 2px /* 默认css布局，会被js设置覆盖 */
      box-sizing: border-box
      display: flex
      align-items: center
      justify-content: center
      .inner
        display: flex
        align-items: center
        justify-content: center
        width: 100%
        height: 100%
        background : #eee
        img
          width: 100%
          height: 100%
</style>